/*
 *
 * System2 Simulation Framework
 * Copyright (C) 2011 - 2012 Jacob Dawid
 * jacob.dawid@googlemail.com
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#include "packagemanager.h"
#include "communication.h"

#include <dlfcn.h>
#include <iostream>

namespace System2 {
    PackageManager PackageManager::m_instance;
    PackageManager::PackageManager() {
        m_packagesTreeModel = new PackagesTreeModel();
    }

    PackageManager::PackageManager(const PackageManager& other) {
        (void)other;
    }

    PackageManager::~PackageManager() {
        delete m_packagesTreeModel;
    }

    PackageManager *PackageManager::instance() {
        return &m_instance;
    }

    bool PackageManager::loadPackage(const QString& fileName, const char *key) {
        Communication *communication = Communication::communication();
        void *handle = dlopen(fileName.toStdString().c_str(), RTLD_NOW);
        if(!handle) {
            communication->complain(QString("Error loading module: %1").arg(dlerror()));
            return false;
        }

        void *loader = dlsym(handle, "packageLoader");
        if(!loader) {
            communication->complain("This package does not appear to be valid.");
            return false;
        }

        typedef Package* (*LoaderFunction)(const char *key);
        LoaderFunction loaderFunction = (LoaderFunction)loader;

        Package *package = loaderFunction(key);

        if(!package) {
            communication->complain("You are not authorized to load this package.");
            return false;
        }

        QStandardItem *rootItem = m_packagesTreeModel->invisibleRootItem();
        QStandardItem *packageItem = new QStandardItem(package->displayName());
        rootItem->appendRow(packageItem);

        foreach(Component *c, package->components()) {
            QStandardItem *standardItem
                    = new QStandardItem(QIcon(c->symbol()), c->displayName());
            standardItem->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsDragEnabled);
            standardItem->setData(c->signature());
            packageItem->appendRow(standardItem);
        }

        m_loadedPackages[package->signature()] = package;

        communication->notify(
        QString("Successfully loaded package \'%1\' with signature \'%2\' containing %3 components.")
            .arg(package->displayName())
            .arg(package->signature())
            .arg(package->components().size()));



        QList<Component*> components = package->components();
        foreach(Component* component, components) {
            communication->notify(QString("- Found \'%1\' (%2)")
                      .arg(component->displayName())
                      .arg(component->signature()));
        }

        package->storeDynamicLibraryHandle(handle);
        return true;
    }

    QList<Package*> PackageManager::loadedPackages() {
        return m_loadedPackages.values();
    }

    QList<QString> PackageManager::packageSignatures() {
        return m_loadedPackages.keys();
    }

    QList<QString> PackageManager::componentSignatures() {
        QList<QString> result;
        foreach(Package* package, m_loadedPackages) {
            result.append(package->componentSignatures());
        }
        return result;
    }

    Package *PackageManager::packageBySignature(const QString& signature) {
        if(m_loadedPackages.contains(signature)) {
            return m_loadedPackages[signature];
        }
        return 0;
    }

    Component *PackageManager::componentBySignature(const QString& signature) {
        foreach(Package* package, m_loadedPackages) {
            Component *component = package->componentBySignature(signature);
            if(component)
                return component;
        }
        return 0;
    }

    PackagesTreeModel *PackageManager::packagesTreeModel() {
        return m_packagesTreeModel;
    }
}
